using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BardsTaleViewer {
  public partial class Form1 : Form {
    //private byte [] _dumpLeft = null;
    //private byte [] _dumpRight = null;
    //private int _nbLeft = 0;
    //private int _nbRight = 0;
    //private IPicture _pictureLeft = null;
    //private IPicture _pictureRight = null;
    private PicturePlayer _picturePlayerLeft = null;
    private PicturePlayer _picturePlayerRight = null;
    private Timer _timer = null;
    private int _pictureIndex;
    private int _bardVersion;

    private bool _internal = false;

    private LoaderVersion _loaderVersionLeft = null;
    private LoaderVersion _loaderVersionRight = null;

    public Form1 () {
      InitializeComponent ();

      _bwUI.DoWork += new DoWorkEventHandler (_bwUI_DoWork);
      _bwUI.ProgressChanged += new ProgressChangedEventHandler (_bwUI_ProgressChanged);
      _bwUI.RunWorkerCompleted += new RunWorkerCompletedEventHandler (_bwUI_RunWorkerCompleted);

      _versionUI.SelectedIndex = 0;
    }

    public void SetVersion (int bardVersion) {
      _bardVersion = bardVersion;
      _loaderVersionLeft = new LoaderVersion (bardVersion, SystemEnum.Amiga);
      if (bardVersion == 1)
        _loaderVersionRight = new LoaderVersion (bardVersion, SystemEnum.Atari);
      else if (bardVersion == 4)
        _loaderVersionRight = new LoaderVersion (bardVersion, SystemEnum.PC);
      else
        _loaderVersionRight = null;

      //_nbLeft = 0;
      //if (_dumpLeft != null)
      //  if (_bardVersion == 3)
      //    _nbLeft = (int) _dumpLeft.ReadLong (0) / 4 - 1;
      //  else
      //    _nbLeft = (int) _dumpLeft.ReadLong (0) / 4;
      //else
      //  if (_bardVersion == 4)
      //    _nbLeft = Picture_Amiga._filenamesBard4.Length;

      //_nbRight = 0;
      //if (dumpRight != null)
      //  _nbRight = (int) dumpRight.ReadLong (0) / 4;
      //else
      //  if (_bardVersion == 4)
      //    _nbRight = Picture_Amiga._filenamesBard4.Length;

      //int nbMax = (_nbLeft > _nbRight) ? _nbLeft : _nbRight;


      //_doubleBufferedPanelLeft.c ();
      //_doubleBufferedPanelRight.Invalidate ();

      _internal = true;
      _pictureUI.Value = 0;
      _pictureUI.Minimum = 0;
      _pictureUI.Maximum = 0;
      _internal = false;

      int nbMax = SetPicture (0);

      _internal = true;
      _pictureUI.Maximum = nbMax + 1;
      _pictureUI.Value = 1;
      _internal = false;
    }

    public static int GetDelay (int bardVersion) {
      if (bardVersion == 1)
        return (127);
      else if (bardVersion == 2)
        return (94);
      else if (bardVersion == 3)
        return (188 * 2);
      else if (bardVersion == 4)
        return (79);
      throw new Exception ();
    }

    public int SetPicture (int pictureIndex) {
      if (_timer != null) {
        _timer.Stop ();
        _timer.Dispose ();
      }

      _pictureIndex = pictureIndex;

      if (_loaderVersionLeft != null)
        _loaderVersionLeft.LoadPicture (pictureIndex);
      if (_loaderVersionRight != null)
        _loaderVersionRight.LoadPicture (pictureIndex);

      //if (_pictureIndex < _nbLeft) {
      //  if (_bardVersion != 4) {
      //    if ((_bardVersion == 2) &&
      //     ((_pictureIndex == 25) ||
      //      (_pictureIndex == 30) ||
      //      (_pictureIndex == 52)))
      //      _pictureLeft = null;
      //    else
      //      _pictureLeft = Picture_Amiga.Load (null, _dumpLeft, _pictureIndex, _bardVersion);
      //  }
      //  else {
      //    filename = Picture_Amiga._filenamesBard4 [_pictureIndex];
      //    _pictureLeft = Picture_Amiga.Load (filename, null, _pictureIndex, _bardVersion);
      //  }
      //}
      //else
      //  _pictureLeft = null;

      //if (_pictureIndex < _nbRight) {
      //  if (_bardVersion != 4)
      //    _pictureRight = Picture_Atari.Load (_dumpRight, _pictureIndex);
      //  else {
      //    filename = Picture_Amiga._filenamesBard4 [_pictureIndex];
      //    _pictureRight = Picture_PC.Load (filename, _pictureIndex);
      //  }
      //}
      //else
      //  _pictureRight = null;

      string filename = null;
      int nbMax = 0;
      string textLeft = null;
      string textRight = null;

      _picturePlayerLeft = null;
      if (_loaderVersionLeft != null) {
        if (nbMax < _loaderVersionLeft._nbMax)
          nbMax = _loaderVersionLeft._nbMax;
        textLeft = _loaderVersionLeft._system.ToString ();
        if (_loaderVersionLeft._picture != null) {
          _picturePlayerLeft = new PicturePlayer (_loaderVersionLeft._picture);
          if (_loaderVersionLeft._filename != null)
            filename = _loaderVersionLeft._filename;
          if (_loaderVersionLeft._picture.Anims == null)
            textLeft += ", NO ANIM";
        }
        else
          textLeft += ", NO PICTURE";
      }

      _picturePlayerRight = null;
      if (_loaderVersionRight != null) {
        if (nbMax < _loaderVersionRight._nbMax)
          nbMax = _loaderVersionRight._nbMax;
        textRight = _loaderVersionRight._system.ToString ();
        if (_loaderVersionRight._picture != null) {
          _picturePlayerRight = new PicturePlayer (_loaderVersionRight._picture);
          if (_loaderVersionRight._filename != null)
            filename = _loaderVersionRight._filename;
          if (_loaderVersionRight._picture.Anims == null)
            textRight += ", NO ANIM";
        }
        else
          textRight += ", NO PICTURE";
      }

      _doubleBufferedPanelLeft.Invalidate ();
      _doubleBufferedPanelRight.Invalidate ();

      string title = string.Format ("Picture: {0}/{1}", _pictureIndex + 1, nbMax);
      if (filename != null)
        title += string.Format (", Name: \"{0}\"", filename);

      Text = title;
      _textLeftUI.Text = textLeft;
      _textRightUI.Text = textRight;

      _timer = new Timer ();
      _timer.Interval = GetDelay (_bardVersion);
      _timer.Tick += new EventHandler (_timer_Tick);
      _timer.Start ();

      return (nbMax);
    }

    //public static void LoadPicture (SystemEnum system, int bardVersion, int pictureIndex) {
    //  IPicture picture = null;
    //  string filename = null;
    //  if (pictureIndex < _nbLeft) {
    //    if (bardVersion != 4) {
    //      if ((bardVersion == 2) &&
    //       ((pictureIndex == 25) ||
    //        (pictureIndex == 30) ||
    //        (pictureIndex == 52)))
    //        _pictureLeft = null;
    //      else
    //        _pictureLeft = Picture_Amiga.Load (null, _dumpLeft, pictureIndex, bardVersion);
    //    }
    //    else {
    //      filename = Picture_Amiga._filenamesBard4 [pictureIndex];
    //      _pictureLeft = Picture_Amiga.Load (filename, null, pictureIndex, bardVersion);
    //    }
    //  }
    //  else
    //    _pictureLeft = null;

    //  if (pictureIndex < _nbRight) {
    //    if (bardVersion != 4)
    //      _pictureRight = Picture_Atari.Load (_dumpRight, pictureIndex);
    //    else {
    //      filename = Picture_Amiga._filenamesBard4 [pictureIndex];
    //      _pictureRight = Picture_PC.Load (filename, pictureIndex);
    //    }
    //  }
    //  else
    //    _pictureRight = null;

    //}

    public static int ComputePeriod (List<IAnim> anims) {
      int period = ComputeAnimPeriod (anims [0]);
      for (int i = 1; i < anims.Count; i++)
        period = PPCM (period, ComputeAnimPeriod (anims [i]));
      return (period);
    }

    public static int ComputeAnimPeriodMax (List<IAnim> anims) {
      if (anims == null)
        return (1);
      if (anims.Count == 0)
        return (1);
      int periodMax = ComputeAnimPeriod (anims [0]);
      for (int i = 1; i < anims.Count; i++) {
        int periodTemp = ComputeAnimPeriod (anims [i]);
        if (periodMax < periodTemp)
          periodMax = periodTemp;
      }
      return (periodMax);
    }

    public static int ComputeAnimPeriod (IAnim anim) {
      int period = 0;
      foreach (IAnimFrame frame in anim.Frames)
        period += frame.Delay + 1;
      return (period);
    }

    public static int PPCM (int a, int b) {
      if ((a % b) == 0)
        return (a);
      else {
        int t, x, m;
        t = a * b;
        while (b != 0) {
          x = a % b;
          a = b;
          b = x;
        }
        m = t / a;
        return (m);
      }
    }

    void _timer_Tick (object sender, EventArgs e) {
      if (_picturePlayerLeft != null) {
        _picturePlayerLeft.Tick ();
        _doubleBufferedPanelLeft.Invalidate ();
      }
      if (_picturePlayerRight != null) {
        _picturePlayerRight.Tick ();
        _doubleBufferedPanelRight.Invalidate ();
      }
    }

    private void _doubleBufferedPanel_Click (object sender, EventArgs e) {
      //_timer.Start ();
    }

    void _doubleBufferedPanel_Paint (object sender, PaintEventArgs e) {
      if (_picturePlayerLeft != null)
        e.Graphics.DrawImage (_picturePlayerLeft._bitmap, 0, 0, 2);
    }

    private void _doubleBufferedPanelAmiga_Paint (object sender, PaintEventArgs e) {
      if (_picturePlayerRight != null)
        e.Graphics.DrawImage (_picturePlayerRight._bitmap, 0, 0, 2);
    }

    private void Form1_MouseDown (object sender, MouseEventArgs e) {
    }

    private void Form1_PreviewKeyDown (object sender, PreviewKeyDownEventArgs e) {
      if (_timer.Enabled)
        _timer.Stop ();
      else
        _timer.Start ();
    }

    private void _versionUI_SelectedIndexChanged (object sender, EventArgs e) {
      SetVersion (_versionUI.SelectedIndex + 1);
    }

    private void _pictureUI_ValueChanged (object sender, EventArgs e) {
      if (_internal)
        return;

      //int nbMax = (_nbLeft > _nbRight) ? _nbLeft : _nbRight;

      int pictureNum = (int) _pictureUI.Value;
      _internal = true;
      if (pictureNum == _pictureUI.Maximum) {
        _pictureUI.Value = 1;
      }
      if (pictureNum == _pictureUI.Minimum) {
        _pictureUI.Value = _pictureUI.Maximum - 1;
      }
      _internal = false;
      pictureNum = (int) _pictureUI.Value;

      //if (e.Button == MouseButtons.Left) {
      //  _pictureIndex = (_pictureIndex + 1 + nbMax) % nbMax;
      //}
      //else {
      //  _pictureIndex = (_pictureIndex - 1 + nbMax) % nbMax;
      //}
      SetPicture (pictureNum - 1);
    }

    public static void SaveGIF (LoaderVersion loaderVersion) {
      if (loaderVersion == null)
        return;
      if (loaderVersion._picture == null)
        return;

      string filenameGif = String.Format ("Bard {0} {1} {2}", loaderVersion._bardVersion, (loaderVersion._pictureIndex + 1).ToString ().PadLeft (2, '0'), loaderVersion._system.ToString ());
      Gif.CreateAnimatedGif (loaderVersion._picture, filenameGif, loaderVersion._bardVersion);
    }

    private void _saveGifUI_Click (object sender, EventArgs e) {
      SaveGIF (_loaderVersionLeft);
      SaveGIF (_loaderVersionRight);
    }

    private void _saveCurrentUI_Click (object sender, EventArgs e) {
      ProgressBarControl progressBar = new ProgressBarControl (this);
      SetEnabled (false);

      List<LoaderVersion> loaders = new List<LoaderVersion> ();
      if (_loaderVersionLeft != null)
        loaders.Add (new LoaderVersion (_loaderVersionLeft._bardVersion, _loaderVersionLeft._system));
      if (_loaderVersionRight != null)
        loaders.Add (new LoaderVersion (_loaderVersionRight._bardVersion, _loaderVersionRight._system));

      SaveAll (loaders, progressBar);
    }

    private void _saveAllUI_Click (object sender, EventArgs e) {
      ProgressBarControl progressBar = new ProgressBarControl (this);
      SetEnabled (false);

      List<LoaderVersion> loaders = new List<LoaderVersion> ();
      loaders.Add (new LoaderVersion (1, SystemEnum.Amiga));
      loaders.Add (new LoaderVersion (1, SystemEnum.Atari));
      loaders.Add (new LoaderVersion (2, SystemEnum.Amiga));
      loaders.Add (new LoaderVersion (3, SystemEnum.Amiga));
      loaders.Add (new LoaderVersion (4, SystemEnum.Amiga));
      loaders.Add (new LoaderVersion (4, SystemEnum.PC));

      SaveAll (loaders, progressBar);
    }

    public void SaveAll (List<LoaderVersion> loaders, ProgressBarControl progressBar) {
      _bwUI.RunWorkerAsync (new object [] { loaders, progressBar });
    }

    void _bwUI_DoWork (object sender, DoWorkEventArgs e) {
      object [] args = (object []) e.Argument;
      List<LoaderVersion> loaders = (List<LoaderVersion>) (args [0]);
      ProgressBarControl progressBar = (ProgressBarControl) (args [1]);

      LoaderVersion loader = loaders [0];
      for (int pictureIndex = 0; pictureIndex < loader._nbMax; pictureIndex++) {
        _bwUI.ReportProgress ((pictureIndex + 1) * 100 / loader._nbMax, new object [] { loader, progressBar, pictureIndex });
        loader.LoadPicture (pictureIndex);
        SaveGIF (loader);
      }
      e.Result = new object [] { loaders, progressBar };
    }

    void _bwUI_ProgressChanged (object sender, ProgressChangedEventArgs e) {
      object [] args = (object []) e.UserState;
      LoaderVersion loader = (LoaderVersion) (args [0]);
      ProgressBarControl progressBar = (ProgressBarControl) (args [1]);
      int pictureIndex = (int) (args [2]);

      progressBar.State = string.Format ("Saving Bard {0} {1} picture {2} of {3}", loader._bardVersion, loader._system.ToString (), pictureIndex + 1, loader._nbMax);
      progressBar.Progress = e.ProgressPercentage;
    }

    void _bwUI_RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e) {
      object [] args = (object []) e.Result;
      List<LoaderVersion> loaders = (List<LoaderVersion>) (args [0]);
      ProgressBarControl progressBar = (ProgressBarControl) (args [1]);

      loaders.RemoveAt (0);

      if (loaders.Count == 0) {
        this.Controls.Remove (progressBar);
        progressBar.Dispose ();
        SetEnabled (true);
      }
      else {
        SaveAll (loaders, progressBar);
      }
    }

    public void SetEnabled (bool enabled) {
      toolStrip1.Enabled = enabled;
    }
  }

  public enum SystemEnum {
    Atari,
    Amiga,
    PC
  }
}
